﻿using System.Diagnostics;
using MockingBird.Studio.UI.Helpers;

namespace MockingBird.Studio.UI
{
    using System;
    using System.Configuration;
    using System.IO;
    using System.Net;
    using System.Web.Services.Protocols;
    using System.Windows.Forms;
    using System.Xml;
    using MockingBird.Common.Infrastructure.Helpers;
    using MockingBird.Common.Infrastructure.Logging;
    using MockingBird.Common.Infrastructure.Soap;
    using MockingBird.Simulator.Framework;
    using MockingBird.Simulator.Framework.Configuration;

    public partial class ServiceInvoker : ComponentFactory.Krypton.Toolkit.KryptonForm
    {
        private string endpointName;
        private string requestFileName;
        private string targetUrl;
        private string soapAction;
        private SoapRequest soapRequest;
        private bool isOneWay;
        private ApplicationConfiguration simulatorAppConfig;
        private IHandlerConfigurationManager handlerConfigManager;
        private IFolderManager folderManager;
        private ILogger logger;

        #region "Constructor(s)"
        public ServiceInvoker()
        {
            InitializeComponent();
            Trace.WriteLine("Studio: Service Invoker constructor");
            soapRequest = new SoapRequest();
            this.InitializeDependencies();
        }

        private void InitializeDependencies()
        {
            this.TryInitAppConfig();
            this.logger = DependencyHelper.GetLogger();
            this.handlerConfigManager = DependencyHelper.GetHandlerConfigManager(this.simulatorAppConfig);
            this.folderManager = DependencyHelper.GetFolderManager();
            // We need to get the path to the requests folder to set it in the dialog
            // so the folder manager must first be initialized with the root path
            // to enable it to calculate paths relative to that.
            this.folderManager.InitDataFilesRoot(this.simulatorAppConfig.AppRoot);

        }

        private void TryInitAppConfig()
        {
            // This may seem very odd code, but we seem to be struggling with loading the unity config 
            // the first time the app runs. Once we debug through, then magically everything seems to initialize
            // itself. So we keep trying 3 times and then give up.
            Trace.WriteLine("Initializing App Config : Basic Attempt");
            try
            {
                this.InitAppConfig();
            }
            catch (Exception)
            {
                Trace.WriteLine("Exception encountered. Attempt 2");
                this.InitAppConfig();
            }

        }

        private void InitAppConfig()
        {
            this.simulatorAppConfig = DependencyHelper.GetAppConfig();

        }
        #endregion

        #region "event handlers"




        private void Form1_Load(object sender, EventArgs e)
        {
            //Set the file open dialog root folder
            SetupDefaultSettings();
            InitializeDialogProperties();

        }



        private void btnBrowseRequestFile_Click(object sender, EventArgs e)
        {
            //Launch the file open dialog 
            DialogResult result = dlgOpenRequestFile.ShowDialog();
            if (result == DialogResult.OK)
            {

                //get the name of the file to open
                requestFileName = dlgOpenRequestFile.FileName;
                //set it in the text box
                txtRequestFile.Text = requestFileName;
                //if there was an entry then load it up
                if (requestFileName.Length > 0)
                {
                    //Load the file contents into the request text box
                    txtWebRequest.Text = File.ReadAllText(requestFileName);

                }
            }

        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            this.HandleRequestSubmission();
        }

        private void HandleRequestSubmission()
        {
            //first clear the response window
            this.wbResponse.DocumentText = "";
            this.Refresh();
            // Recalculate the target Url to be on the safe side
            this.RecalculateTargetUrl();
            this.soapAction = this.txtSoapAction.Text;

            if (this.IsValid())
            {
                this.pgOperationProgress.Increment(10);
                this.Refresh();
                this.SendSoapRequest();
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            this.IncrementProgress();
        }

        private void IncrementProgress()
        {
            if (this.pgOperationProgress.Value >= 100)
            {
                this.pgOperationProgress.Value = 0;
                return;
            }
            this.pgOperationProgress.Value += 10;
        }

        #endregion

        #region "helper methods"
        private void SetupDefaultSettings()
        {
            // Set the option to ASMX
            cboSimulatorTestMode.SelectedIndex = 0;

            txtRequestFile.Text = ConfigurationManager.AppSettings["default_req_msg_location"];
            txtMockingBirdRootUrl.Text = ConfigurationManager.AppSettings["default_asmx_url"];
            txtSoapAction.Text = ConfigurationManager.AppSettings["default_soapaction"];

            dlgOpenRequestFile.InitialDirectory = txtRequestFile.Text;

            this.LoadAvailableEndpoints();
        }

        private void InitializeDialogProperties()
        {
            //Request File browse
            dlgOpenRequestFile.DefaultExt = "*.xml";
            dlgOpenRequestFile.Filter = "XML files (*.xml)|*.xml";
            dlgOpenRequestFile.FileName = "";


        }

        private void LoadAvailableEndpoints()
        {
            string defaultEndpoint = ConfigurationManager.AppSettings["default_endpoint_name"];
            // Load the entries returned by the simulator app config
            this.cboEndpointName.Items.AddRange(this.simulatorAppConfig.GetRegisteredEndpoints().ToArray());
            if (cboEndpointName.Items.Contains(defaultEndpoint))
            {
                cboEndpointName.SelectedIndex = cboEndpointName.Items.IndexOf(defaultEndpoint);
            }


        }
        private bool IsValid()
        {
            if (String.IsNullOrEmpty(targetUrl))
            {
                MessageBox.Show("Destination url must be entered");
                txtMockingBirdRootUrl.Focus();
                return false;
            }

            if (String.IsNullOrEmpty(txtWebRequest.Text))
            {
                MessageBox.Show("Request Message must be entered");
                txtMockingBirdRootUrl.Focus();
                return false;

            }

            //Soap Action is not mandatory because the Url may contain the method name

            return true;

        }
        private void SendSoapRequest()
        {
            BuildSoapRequest();

            //Now instantiate a SoapHttpDispatcher for that soap Request
            SoapHttpDispatcher dispatcher = new SoapHttpDispatcher(this.soapRequest);
            try
            {
                string response = dispatcher.Dispatch(this.targetUrl);
                //Now load the response directly into the webBrowser using the helper class
                // By default webBrowser control does not allow the DocumentText to be set directly
                // ref: http://blorgh.wordpress.com/2006/10/17/displaying-xml-in-the-net-webbrowser-control/

                // if some weird error occurred where the response was empty, then
                // set a default message
                if (String.IsNullOrEmpty(response))
                {
                    response = "<ServiceStudio> Target returned empty response. This may have been a one way message or an error may have occurred and the response suppressed. </ServiceStudio>";
                }

                XmlDocument xDoc = new XmlDocument();
                xDoc.LoadXml(response);
                wbResponse.DocumentText = WebBrowserHelper.GetXml(xDoc);


            }
            catch (SoapException se)
            {

                wbResponse.DocumentText = se.ToString();
            }
            catch (WebException wex)
            {
                wbResponse.DocumentText = wex.ToString();

            }
            finally
            {
                tabInvoker.SelectedTab = tabPageResponse;
                //tabPageResponse.Select();
            }
        }

        private void BuildSoapRequest()
        {
            XmlDocument bodyDoc = new XmlDocument();
            bodyDoc.LoadXml(txtWebRequest.Text);

            // if there is an XmlDeclaration element then get rid of it as it causes problems
            // when this data is merged into a Soap Request Envelope
            if (bodyDoc.FirstChild is XmlDeclaration)
            {
                XmlDeclaration decl = (XmlDeclaration)bodyDoc.FirstChild;
                bodyDoc.RemoveChild(decl);

            }

            // First create a soap Request
            this.soapRequest.Action = soapAction;
            this.soapRequest.AddBody(bodyDoc);
        }
        #endregion


        private void cboSimulatorTestMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.IsAsmxTarget())
            {
                txtMockingBirdRootUrl.Text = ConfigurationManager.AppSettings["default_asmx_url"];
            }
            else
            {
                txtMockingBirdRootUrl.Text = ConfigurationManager.AppSettings["default_wcf_url"];
            }
            this.RecalculateTargetUrl();
        }

        private void txtMockingBirdRootUrl_Leave(object sender, EventArgs e)
        {
            // ensure that there is an ending forward slash
            if (!txtMockingBirdRootUrl.Text.EndsWith("/"))
            {
                txtMockingBirdRootUrl.Text = txtMockingBirdRootUrl.Text + "/";
                this.Refresh();
            }
            this.RecalculateTargetUrl();
        }

        private void cboEndpointName_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.ResetTabs();
            this.RecalculateTargetUrl();
            this.SetEndpointSpecificData();


        }

        private void ResetTabs()
        {
            //first clear the response window
            this.wbResponse.DocumentText = "";
            this.txtWebRequest.Text = "";
            this.tabInvoker.SelectedTab = this.tabPageRequest;

        }

        private void SetEndpointSpecificData()
        {
            this.SetOperationInference();
            this.SetOperationCharacteristics();

            this.SetSoapActionState();

            this.SetRequestsBaseFolder();
        }


        private void SetOperationInference()
        {
            //string configFileName = this.simulatorAppConfig.GetEndpointConfigFile(this.cboEndpointName.Text);
            string configFileName = this.folderManager.GetConfigFile(this.cboEndpointName.Text);

            if (File.Exists(configFileName))
            {
                // load the data - use the forceLoad option 
                // (this is needed because the UI could be used to create test/create multiple services
                // with different config data so the one we are testing needs to have the correct config data
                // all loaded and ready.
                this.handlerConfigManager.InitializeConfigurationData(configFileName, true);
                this.txtOperationInference.Text = this.handlerConfigManager.GetOperationInferenceStrategy();
            }
            else
            {
                string warningMessage = String.Format("Could not find config file : {0}. Please ensure that the handlerMapConfig file reflects the existing endpoints in the dataFiles folder", configFileName);
                MessageBox.Show(warningMessage, "Warning : Config File Not Found");
            }
        }

        private void SetOperationCharacteristics()
        {
            OperationNameInferenceEngine inferenceEngine = new OperationNameInferenceEngine(this.handlerConfigManager);

            //this.isOneWay = this.handlerConfigManager.IsOneWay()

        }

        private void SetSoapActionState()
        {
            if (!this.txtOperationInference.Text.Equals("SoapAction"))
            {
                this.txtSoapAction.Enabled = false;
            }
        }

        private void SetRequestsBaseFolder()
        {
            string requestFolder = this.folderManager.GetRequestRoot(this.cboEndpointName.Text);
            txtRequestFile.Text = requestFolder;
            dlgOpenRequestFile.FileName = "";
            dlgOpenRequestFile.InitialDirectory = requestFolder;
            this.Refresh();

        }

        private void RecalculateTargetUrl()
        {
            // Get the root URL
            string target = txtMockingBirdRootUrl.Text;
            if (!target.EndsWith("/"))
            {
                target = target + "/";
            }

            // Add the endpoint name to the root
            target = target + cboEndpointName.Text;

            // Now if we are targeting ASMX then ensure we suffix it with .ASMX
            // If we have already registered with .ASMX then even if we are in WCF mode, 
            // we let the url have the .ASMX in it otherwise the MB simulator
            // will not recognize the name
            if (this.IsAsmxTarget())
            {
                if (!target.EndsWith(".asmx"))
                {
                    target = target + ".asmx";
                }
            }

            // Set this into the member and the descriptive label.
            this.targetUrl = target;
            lblFullDestinationUrl.Text = this.targetUrl;
            this.Refresh();
        }

        private bool IsAsmxTarget()
        {
            return cboSimulatorTestMode.Text.Equals("ASMX");

        }

    }
}
